#include <cstring>
#include <iostream>
#include <thread>
#include <unistd.h>

// =============== Windows ==============
#if defined(_WIN32) || defined(_WIN64)
#include <cstdint>
#include <ws2tcpip.h>
#include <winsock2.h>
#include "ws2ipdef.h"
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
#define close(sock) closesocket(sock)
typedef SOCKET socket_t;
#define INVALID_SOCKET_VALUE INVALID_SOCKET
using ushort = unsigned short;
// =========  Unix/Linux/Macos  =========
#else
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <ifaddrs.h> //ipv4
#include <netdb.h>   //ipv4
#include <sys/select.h>
#define INVALID_SOCKET_VALUE -1
typedef int             socket_t;
typedef struct sockaddr SOCKADDR;
#endif

#define BUFFER_SIZE 1024

// 编译： g++ -std=c++11 -pthread udp_client.cpp -o udp_client.exe -lws2_32
int main(int argc, char *argv[]) {
	if (argc != 3) {
		std::cerr << "Usage: " << argv[0] << " <server_ip> <server_port>" << std::endl;
		return -1;
	}

	const char *serverIP   = argv[1];
	int         serverPort = atoi(argv[2]);

	int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (fd < 1) {
		std::cerr << "Create socket failed" << std::endl;
		return -1;
	}

	// 设置服务器地址
	struct sockaddr_in serverAddr;
	serverAddr.sin_family      = AF_INET;
	serverAddr.sin_port        = htons(serverPort);
	serverAddr.sin_addr.s_addr = inet_addr(serverIP);

	// 接收线程(非阻塞)
	std::thread receiver([&]() {
		char buffer[BUFFER_SIZE];
		while (true) {
			ssize_t n = recv(fd, buffer, BUFFER_SIZE, 0);
			if (n < 0) break;
			std::cout.write(buffer, n);
			std::cout << std::flush;
			std::cout << std::endl;
		}
	});

	std::string message;
	while (std::getline(std::cin, message)) {
		if (!message.empty()) {
			auto sentBytes = sendto(fd, message.c_str(), message.size(), 0, (sockaddr *) &serverAddr, sizeof(serverAddr));
			if (sentBytes < 0) {
				std::cerr << "Send failed\n";
				break;
			}
		}
	}

	close(fd);
	receiver.join();
	return 0;
}
